#include<stdlib.h>
#include<string.h>
#include"gc.h"




int isInteger(Value *val){
    return val->t == T_Integer;
}

int isList(Value *val){
    return val->t == T_List;
}

int isString(Value *val){
    return val->t == T_String;
}

int isPair(Value *val){
    return val->t == T_Pair;
}

int isChar(Value *val){
    return val->t == T_Char;
}
int isFloat(Value *val){
    return val->t == T_Float;
}
int isSymbol(Value *val){
    return val->t == T_Symbol;
}
int isLambda(Value *val){
    return val->t == T_Lambda;
}
int isNil(Value *val){
    return val->t == T_Nil;
}

int listLength(Value *val){
    return val->length;
}

Value *listnth(Value *v,int n){
    std::list<Value *>::iterator it=v->val.listval->begin();
    advance(it,n);
    return *it;
}

int integerValue(Value *val){
    return val->val.ival;
}

float floatValue(Value *val){
    return val->val.fval;
}

void ins_add(void *vm){
    SecdVM *v = (SecdVM *)vm;
    v->Code = v->Code->next;
    Value *args = v->Stack->top();
    v->Stack->pop();
    displayValue(args);
    int ri= 0 ;
    float rf=0;
    int retIsFloat = 0;
    if(isList(args)){
        for(int i =0;i<listLength(args);++i ){
            Value *item = listnth(args,i);
            if(isInteger(item)){
                printf("get integer : %d\n",integerValue(item));
                if(i==0){
                    ri = integerValue(item);
                    rf = integerValue(item);
                }else{
                    ri += integerValue(item);
                    rf += integerValue(item);
                }
            }else if(isFloat(item)){
                retIsFloat = 1;
                if(i==0){
                    rf = floatValue(item);
                }else{
                    rf += floatValue(item);
                }
            }
        }
    }
    Value *ret = NULL;
    if(retIsFloat == 1){
        printf("add:%f\n",rf);
        ret = v->makeFloat(rf);
    }else{
        printf("add:%d\n",ri);
        ret = v->makeInteger(ri);
    }
    v->Stack->push(ret);
}

void ins_sub(void *vm){
    SecdVM *v = (SecdVM *)vm;
    v->Code = v->Code->next;
    Value *args = v->Stack->top();
    v->Stack->pop();
    displayValue(args);
    int ri= 0 ;
    float rf=0;
    int retIsFloat = 0;
    if(isList(args)){
        for(int i =0;i<listLength(args);++i ){
            Value *item = listnth(args,i);
            if(isInteger(item)){
                printf("get integer : %d\n",integerValue(item));
                if(i==0){
                    ri = integerValue(item);
                    rf = integerValue(item);
                }else{
                    ri -= integerValue(item);
                    rf -= integerValue(item);
                }
            }else if(isFloat(item)){
                retIsFloat = 1;
                if(i==0){
                    rf = floatValue(item);
                }else{
                    rf -= floatValue(item);
                }
            }
        }
    }
    Value *ret = NULL;
    if(retIsFloat == 1){
        printf("add:%f\n",rf);
        ret = v->makeFloat(rf);
    }else{
        printf("add:%d\n",ri);
        ret = v->makeInteger(ri);
    }
    v->Stack->push(ret);
}

void ins_mul(void *vm){
    SecdVM *v = (SecdVM *)vm;
    v->Code = v->Code->next;
    Value *args = v->Stack->top();
    v->Stack->pop();
    displayValue(args);
    int ri= 0 ;
    float rf=0;
    int retIsFloat = 0;
    if(isList(args)){
        for(int i =0;i<listLength(args);++i ){
            Value *item = listnth(args,i);
            if(isInteger(item)){
                printf("get integer : %d\n",integerValue(item));
                if(i==0){
                    ri = integerValue(item);
                    rf = integerValue(item);
                }else{
                    ri *= integerValue(item);
                    rf *= integerValue(item);
                }
            }else if(isFloat(item)){
                retIsFloat = 1;
                if(i==0){
                    rf = floatValue(item);
                }else{
                    rf *= floatValue(item);
                }
            }
        }
    }
    Value *ret = NULL;
    if(retIsFloat == 1){
        ret = v->makeFloat(rf);
    }else{
        ret = v->makeInteger(ri);
    }
    v->Stack->push(ret);
}

void ins_div(void *vm){
    SecdVM *v = (SecdVM *)vm;
    v->Code = v->Code->next;
    Value *args = v->Stack->top();
    v->Stack->pop();
    displayValue(args);
    int ri= 0 ;
    float rf=0;
    int retIsFloat = 0;
    if(isList(args)){
        for(int i =0;i<listLength(args);++i ){
            Value *item = listnth(args,i);
            if(isInteger(item)){
                printf("get integer : %d\n",integerValue(item));
                if(i==0){
                    ri = integerValue(item);
                    rf = integerValue(item);
                }else{
                    ri /= integerValue(item);
                    rf /= integerValue(item);
                }
            }else if(isFloat(item)){
                retIsFloat = 1;
                if(i==0){
                    rf = floatValue(item);
                }else{
                    rf /= floatValue(item);
                }
            }
        }
    }
    Value *ret = NULL;
    if(retIsFloat == 1){
        printf("add:%f\n",rf);
        ret = v->makeFloat(rf);
    }else{
        printf("add:%d\n",ri);
        ret = v->makeInteger(ri);
    }
    v->Stack->push(ret);
}

void ins_cons(void *vm){

    SecdVM *v = (SecdVM *)vm;
    //printf("enter cons,stack size:%d.\n",v->Stack->size());
    Value *arg1 = v->Stack->top();
    //displayValue(arg1);
    v->Stack->pop();
    //printf("pop 1,stack size:%d.\n",v->Stack->size());
    Value *arg2 = v->Stack->top();
    //displayValue(arg2);
    v->Stack->pop();
    v->Stack->push(v->cons(arg1,arg2));
    v->Code = v->Code->next;
}

void ins_ldi(void *vm){
    SecdVM *v = (SecdVM *)vm;
    v->Stack->push(v->makeInteger(v->Code->constant.ival));
    v->Code = v->Code->next;

}


SecdVM::SecdVM(){
    this->gc=new SimpleGc(this);
    this->NIL = ((SimpleGc *)this->gc)->NIL;
    this->registInstruction(I_ADD,ins_add);
    this->registInstruction(I_CONS,ins_cons);
    this->registInstruction(I_LDI,ins_ldi);
    this->Stack = new std::stack<Value *>();
    this->Code = NULL;
    this->Dump = new std::list<DumpItem *>();
    this->Env = new std::list<Value *>();
}



SecdVM::~SecdVM(){}


void SecdVM::reset(){
    this->Stack = new std::stack<Value *>();
    this->Code = NULL;
    this->Dump = new std::list<DumpItem *>();
    this->Env = new std::list<Value *>();
}

Value *SecdVM::makeInteger(int val){
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    ret->t = T_Integer;
    ret->val.ival = val;
    printf("make integer:%d\n",val);
    return ret;
}

void SecdVM::registInstruction(Instruction inst,instruct_func f){
    this->instructions[inst] = f;
}

Value *SecdVM::makeSymbol(char *symbol,int len){
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    ret->t = T_Symbol;
    ret->length = len;
    ret->val.symbolval = (char *)malloc(len+1);
    memset(ret->val.symbolval,0,len+1);
    memcpy(ret->val.symbolval,symbol,len);

    return ret;
}


Value *SecdVM::makeString(char *str,int len){
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    ret->t = T_String;
    ret->length = len;
    ret->val.symbolval = (char *)malloc(len+1);
    memset(ret->val.symbolval,0,len+1);
    memcpy(ret->val.symbolval,str,len);

    return ret;
}

Value *SecdVM::makeChar(char c){
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    ret->t = T_Char;
    ret->val.cval = c;

    return ret;
}
Value *SecdVM::makeFloat(float f){
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    ret->t = T_Float;
    ret->val.fval = f;

    return ret;
}

Value *SecdVM::makeLambda(Codes *f,std::list<Value *> *env){
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    ret->t = T_Lambda;
    ret->val.closureval = f;
    ret->env = new std::list<Value *>(env->begin(),env->end());
    return ret;
}

Value *SecdVM::cons(Value *a,Value *b){
    //cons (a . list) = a.list
    //cons (a . b)= a.b
    Value *ret = (Value *)((SimpleGc *)this->gc)->malloc(sizeof(Value));
    if(isList(b)){
        ret->t = T_List;
        ret->val.listval = new std::list<Value *>(b->val.listval->begin(),b->val.listval->end());
        ret->val.listval->push_front(a);
        ret->length = b->length + 1;
    }else if(isNil(b)){
        ret->t = T_List;
        ret->val.listval = new std::list<Value *>();
        ret->val.listval->push_front(a);
        ret->length = 1;
    }else{
        ret->t = T_Pair;
        ret->val.pairval = new std::list<Value *>();
        ret->val.pairval->push_front(b);
        ret->val.pairval->push_front(a);
        ret->length = 2;
    }
    return ret;
}



void SecdVM::run(Codes *code){
    this->Code = code;
    int stop = 0;
    while(stop == 0){
        switch(this->Code->inst){
            case I_NIL:
                this->Stack->push(this->NIL);
                this->Code = this->Code->next;
                break;
            case I_STOP:
                stop = 1;
                break;
            default:
                this->instructions[this->Code->inst](this);
        }
    }
}

void displayValue(Value *val){
    std::list<Value *>::iterator itor;
    switch(val->t){
        case T_Integer:
            printf("<%d>\n",val->val.ival);
            break;
    case T_Nil:
        printf("<NIL>\n");
        break;
    case T_List:
        printf("<[");
        if(val->length > 0){

             itor = val->val.listval->begin();
                 while(itor!=val->val.listval->end())
                 {
                     displayValue(*itor);
                     ++itor;
                 }
        }
        printf(")]>\n");
        break;
    }
}

Codes *makeLdi(int n){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDI;
    ret->constant.ival = n;
    ret->next = NULL;
    return ret;
}
Codes *makeNil(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_NIL;
    ret->next = NULL;
    return ret;
}
Codes *makeAdd(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_ADD;
    ret->next = NULL;
    return ret;
}
Codes *makeCons(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_CONS;
    ret->next = NULL;
    return ret;
}
Codes *makeStop(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_STOP;
    ret->next = NULL;
    return ret;
}
Codes *makeSel(Codes *left,Codes *right){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_SEL;
    ret->arg1 = left;
    ret->arg2 = right;
    ret->next = NULL;
    return ret;
}

Codes *makeJoin(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_JOIN;
    ret->next = NULL;
    return ret;
}
Codes *makeSub(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_SUB;
    ret->next = NULL;
    return ret;
}
Codes *makeDiv(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_DIV;
    ret->next = NULL;
    return ret;
}
Codes *makeMul(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_MUL;
    ret->next = NULL;
    return ret;
}
Codes *makeMod(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_MOD;
    ret->next = NULL;
    return ret;
}
Codes *makeLd(int level,int position){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LD;
    ret->next = NULL;
    return ret;
}
Codes *makeLds(char *s){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDS;
    ret->next = NULL;
    ret->constant.sval = s;
    return ret;
}
Codes *makeLdc(char c){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDC;
    ret->next = NULL;
    ret->constant.cval = c;
    return ret;
}
Codes *makeLdf(Codes *c){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDF;
    ret->next = NULL;
    ret->arg1 = c;
    return ret;
}
Codes *makeLdsy(char *symbol){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDC;
    ret->next = NULL;
    ret->constant.sval = symbol;
    return ret;
}
Codes *makeLdb(int b){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDB;
    ret->next = NULL;
    ret->constant.ival = b;
    return ret;
}
Codes *makeLdd(float f){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDD;
    ret->next = NULL;
    ret->constant.fval = f;
    return ret;
}
Codes *makeLdN(char *n){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LDN;
    ret->next = NULL;
    ret->constant.sval = n;
    return ret;
}
Codes *makeAp(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_AP;
    ret->next = NULL;
    return ret;
}
Codes *makeDum(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_DUM;
    ret->next = NULL;
    return ret;
}
Codes *makeRap(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_RAP;
    ret->next = NULL;
    return ret;
}
Codes *makeTap(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_TAP;
    ret->next = NULL;
    return ret;
}
Codes *makeDap(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_DAP;
    ret->next = NULL;
    return ret;
}
Codes *makeEq(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_EQ;
    ret->next = NULL;
    return ret;
}
Codes *makeGt(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_GT;
    ret->next = NULL;
    return ret;
}
Codes *makeLt(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LT;
    ret->next = NULL;
    return ret;
}
Codes *makeGe(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_GE;
    ret->next = NULL;
    return ret;
}
Codes *makeLe(){
    Codes *ret = (Codes *)malloc(sizeof(Codes));
    ret->inst= I_LE;
    ret->next = NULL;
    return ret;
}


